[{"data":1,"prerenderedAt":612},["ShallowReactive",2],{"featured-en":3},[4,315,488],{"id":5,"title":6,"body":7,"category":262,"cover":263,"coverAlt":264,"description":265,"draft":266,"extension":267,"externalUrl":268,"featured":269,"highlights":270,"links":280,"meta":299,"navigation":269,"order":300,"org":301,"path":302,"period":303,"role":304,"seo":305,"stack":306,"stem":313,"__hash__":314},"projects\u002Fen\u002Fprojects\u002Fci-translation-components.md","CI Translation Components",{"type":8,"value":9,"toc":255},"minimark",[10,15,19,22,30,33,37,43,49,65,71,93,99,169,176,201,207,213,217,220,252],[11,12,14],"h2",{"id":13},"what","What",[16,17,18],"p",{},"Translation at most companies lives outside the tools engineers actually use. Content is exported, mailed to a vendor, translated somewhere else, and pasted back in weeks later — by which point the source has moved on. The result is localized content that is permanently, structurally stale.",[16,20,21],{},"CI Translation Components started as my submission to GitLab's 2026 AI Enterprise Hackathon: a set of reusable GitLab CI\u002FCD components that treat translation as a pipeline stage rather than a side quest. A source file changes, CI notices, the content is translated, and the translation arrives as an ordinary merge request in the same repository — reviewable, revertible, and auditable like any other change.",[16,23,24,25,29],{},"The trial was deliberately narrow. The goal for v1 was not to solve every localization use case; it was to prove that a CI component could reliably handle a small, well-bounded set of ",[26,27,28],"strong",{},"real production content"," and create the right merge requests without an engineer standing over it. Building something customers could adopt meant using it ourselves first.",[16,31,32],{},"I wrote the roadmap and carried the epic as overall DRI, sharing ownership with the Linguistic Quality Assurance DRI who owned translation quality.",[11,34,36],{"id":35},"how","How",[16,38,39,42],{},[26,40,41],{},"Pipeline shape."," Source change → detect → translate → commit → merge request → auto-merge. Every model call routes through the GitLab AI Gateway; the runner never talks to a vendor API directly, which is what made the whole thing defensible from a security review perspective.",[16,44,45,48],{},[26,46,47],{},"Quality starts before the model sees a word."," Nothing is translated cold. Every job is grounded in three inputs: a set of specifications for what to translate and how, a termbase maintained in GitLab, and translation instructions that set tone, style and locale conventions. Because all three live in version control, terminology and voice are reviewable, diffable and owned — just like the content they shape.",[16,50,51,54,55,59,60,64],{},[26,52,53],{},"Review stays adjacent, not blocking."," This was the design decision the trial turned on. If a linguist has to sign off before a translation MR can merge, throughput collapses to the speed of human review and you have rebuilt the vendor bottleneck inside CI. Instead, the first review pass happens where the translation lands: GitLab Duo reviews the merge request as soon as it arrives, and on its own it does a genuinely solid job. Linguistic review then fires ",[56,57,58],"em",{},"after"," merge — a Translation Review Flagger flow opens async tracking issues in a separate ",[61,62,63],"code",{},"linguistic-review-tracker"," project — so quality problems get found and fixed without holding the pipeline hostage.",[16,66,67,70],{},[26,68,69],{},"The quality bar is configurable, not fixed."," Teams that need a higher bar have plenty of levers, all native to GitLab: custom MR review instructions for Duo, additional CI jobs that gate on their own checks, Vale rules that enforce style and terminology, and the usual approval rules. The component sets a sensible default; each project decides how tight to hold the line.",[16,72,73,76,77,80,81,84,85,88,89,92],{},[26,74,75],{},"Blind spots before production."," Before touching anything customer-facing, two parallel workstreams ran the components against forks — Spanish Solutions pages on an ",[61,78,79],{},"about.gitlab.com"," fork, and ",[61,82,83],{},"\u002Fdocs"," on a GitLab Operator test fork. Forks first meant the failure modes we found were free. The one hard prerequisite was unglamorous: group-level ",[61,86,87],{},"AI_API_KEY"," and ",[61,90,91],{},"GITLAB_API_TOKEN"," provisioning, which blocked every production pipeline until it landed.",[16,94,95,98],{},[26,96,97],{},"First production wave."," Four projects, chosen because they were real but bounded:",[100,101,102,118],"table",{},[103,104,105],"thead",{},[106,107,108,112,115],"tr",{},[109,110,111],"th",{},"Project",[109,113,114],{},"Content",[109,116,117],{},"Languages",[119,120,121,134,148,159],"tbody",{},[106,122,123,128,131],{},[124,125,126],"td",{},[61,127,79],{},[124,129,130],{},"44 Solutions pages across 7 categories",[124,132,133],{},"Spanish",[106,135,136,139,145],{},[124,137,138],{},"GitLab Operator",[124,140,141,142],{},"11 documentation pages, excluding ",[61,143,144],{},"\u002Fdeveloper",[124,146,147],{},"Korean, French, Japanese",[106,149,150,153,156],{},[124,151,152],{},"Orbit",[124,154,155],{},"29 documentation pages, the full docs set",[124,157,158],{},"Japanese",[106,160,161,164,167],{},[124,162,163],{},"GitLab releases",[124,165,166],{},"3 release posts, 19.2–19.4, simshipped with English",[124,168,158],{},[16,170,171,172,175],{},"Orbit was the proof of ",[56,173,174],{},"continuous",": I translated its entire documentation set into Japanese and kept it current as the source changed, with translation MRs typically landing about 20 minutes after the source commit.",[16,177,178,181,182,189,190,88,195,200],{},[26,179,180],{},"Linguists shipping releases, end to end."," The biggest proof point wasn't a page count — it was who was driving. I set our linguists up to configure these pipelines themselves and self-serve translations from source change to merged MR, with no engineer in the loop. They used it to simship GitLab's ",[183,184,188],"a",{"href":185,"rel":186},"https:\u002F\u002Fdocs.gitlab.com\u002Fja-jp\u002Freleases\u002F19\u002Fgitlab-19-2-released\u002F",[187],"nofollow","19.2",", ",[183,191,194],{"href":192,"rel":193},"https:\u002F\u002Fdocs.gitlab.com\u002Fja-jp\u002Freleases\u002F19\u002Fgitlab-19-3-released\u002F",[187],"19.3",[183,196,199],{"href":197,"rel":198},"https:\u002F\u002Fdocs.gitlab.com\u002Fja-jp\u002Freleases\u002F19\u002Fgitlab-19-4-released\u002F",[187],"19.4"," releases in Japanese, live on the same day as the English original rather than weeks behind it. That is the shift the project was built for: localization owned by the people who own the language, running on the same platform as everything else.",[16,202,203,206],{},[26,204,205],{},"Rollout in three phases."," Blind-spot identification, then production enablement, then full operational coverage — each with a date and a named owner rather than a vague \"when it's ready\".",[16,208,209,212],{},[26,210,211],{},"Security posture up front."," Least-privilege access for the component and its service accounts, secrets masked and hidden, every run traceable from request through pipeline to MR, and a rollback path that any DRI could execute immediately. Writing these down before rollout is what let the trial move fast afterwards.",[11,214,216],{"id":215},"outcomes","Outcomes",[16,218,219],{},"The trial ran its window and the epic closed in August 2026. What it produced:",[221,222,223,230,236,242],"ul",{},[224,225,226,229],"li",{},[26,227,228],{},"CI Translation Components running in production"," on real projects, with translations landing as merge requests in the repositories that own the content.",[224,231,232,235],{},[26,233,234],{},"A documented, repeatable workflow"," from request → CI execution → translation MR → review → merge, with named owners, monitoring and support paths — the thing that was missing when this was just a hackathon demo.",[224,237,238,241],{},[26,239,240],{},"Runbooks aimed at customers, not just us",": how to adopt the component, how to troubleshoot a failed job, and how to request or contribute changes. The point of dogfooding was always to hand it onward.",[224,243,244,247,248,251],{},[26,245,246],{},"A clear path forward."," The trial ended with a written ",[56,249,250],{},"stabilize and expand"," epic rather than a victory lap, which is the honest outcome: production taught us about token rotation pausing forks, post-processing that needed hardening, and configuration policy that had to be decided deliberately rather than inherited. Each lesson is captured as scoped, documented work, so whoever picks the components up next starts from a plan and a working production baseline — not a blank page.",[16,253,254],{},"The targets the trial was measured against, set before it started: pipeline success rate of at least 50% of production runs completing without manual engineering intervention, MR creation succeeding for at least 80% of successful runs, and a median turnaround from source change to open MR inside one business day, with minutes as the stretch goal. Orbit's Japanese docs routinely hit the stretch goal, turning around in about 20 minutes.",{"title":256,"searchDepth":257,"depth":257,"links":258},"",2,[259,260,261],{"id":13,"depth":257,"text":14},{"id":35,"depth":257,"text":36},{"id":215,"depth":257,"text":216},"platform","\u002Fimages\u002Fprojects\u002Fplatform-ci-translation-components.svg","One CI component, grounded in specifications, a termbase and translation instructions, turning changes in Orbit, about.gitlab.com and GitLab Operator into Japanese, Korean and French merge requests, each reviewed by GitLab Duo","Turning a hackathon prototype into a production translation pipeline that ships localized content as ordinary merge requests.",false,"md",null,true,[271,274,277],{"value":272,"label":273},"87","pages under continuous translation in the first wave",{"value":275,"label":276},"4","target languages: es, ja, ko, fr",{"value":278,"label":279},"~20 min","typical turnaround from source change to translation MR",[281,284,287,290,292,294,296],{"label":282,"url":283},"ci-translation-components","https:\u002F\u002Fgitlab.com\u002Fgitlab-com\u002Flocalization\u002Fci-translation-components",{"label":285,"url":286},"Epic — Production Rollout Trial","https:\u002F\u002Fgitlab.com\u002Fgroups\u002Fgitlab-com\u002Flocalization\u002F-\u002Fwork_items\u002F174",{"label":288,"url":289},"Roadmap issue","https:\u002F\u002Fgitlab.com\u002Fgitlab-com\u002Flocalization\u002Flocalization-team\u002F-\u002Fwork_items\u002F730",{"label":291,"url":185},"GitLab 19.2 release post (ja-jp)",{"label":293,"url":192},"GitLab 19.3 release post (ja-jp)",{"label":295,"url":197},"GitLab 19.4 release post (ja-jp)",{"label":297,"url":298},"Follow-on — stabilize and expand","https:\u002F\u002Fgitlab.com\u002Fgroups\u002Fgitlab-com\u002Flocalization\u002F-\u002Fwork_items\u002F176",{},1,"GitLab","\u002Fen\u002Fprojects\u002Fci-translation-components","2026","Author and overall DRI",{"title":6,"description":265},[307,308,309,310,311,312],"GitLab CI\u002FCD Components","GitLab AI Gateway","GitLab Runner","Hugo","Markdown","YAML","en\u002Fprojects\u002Fci-translation-components","eovVnNnEZ1in40dmWylMf1sFiWXgg5t_QrJcW6Tqovg",{"id":316,"title":317,"body":318,"category":262,"cover":449,"coverAlt":450,"description":451,"draft":266,"extension":267,"externalUrl":268,"featured":269,"highlights":452,"links":461,"meta":476,"navigation":269,"order":257,"org":301,"path":477,"period":478,"role":479,"seo":480,"stack":481,"stem":486,"__hash__":487},"projects\u002Fen\u002Fprojects\u002Fjapanese-documentation.md","Japanese product documentation",{"type":8,"value":319,"toc":444},[320,322,325,331,338,340,358,376,400,410,412],[11,321,14],{"id":13},[16,323,324],{},"GitLab's product is localized. Its documentation was not — which meant that in GitLab's largest non-English markets, a customer could use the product in their own language and then hit a wall of English the moment they needed to look something up. Research is unambiguous that prospective customers are more likely to buy when they can do so in their native language, and the competitive baseline had already moved: GitHub began localizing its docs in 2022 and now ships eight languages.",[16,326,327,328,330],{},"Localization and Technical Writing partnered on a programme to localize the documentation site for GitLab's tier-one markets — Japanese, German and French — starting with ",[26,329,158],{},", after the docs site's migration to Hugo made a multilingual build possible at all.",[16,332,333,334,337],{},"I worked the engineering side: the site plumbing that makes a second language behave correctly, the cross-domain handoff from ",[61,335,336],{},"gitlab.com\u002Fhelp",", and the launch itself.",[11,339,36],{"id":35},[16,341,342,345,346,349,350,353,354,357],{},[26,343,344],{},"Turning the locale on was one line. Earning the right to flip it took fourteen months."," The merge request that launched Japanese set ",[61,347,348],{},"disabled: false"," for the ",[61,351,352],{},"ja-jp"," language in ",[61,355,356],{},"config\u002F_default\u002Fhugo.yaml",". Everything interesting was in what had to be true first.",[16,359,360,363,364,367,368,371,372,375],{},[26,361,362],{},"Fixing the language selector before anyone used it."," The selector originally dropped you at the top of the target-language page. On documentation — where a reader is usually deep in a specific section of a very long page — that is a quietly infuriating bug. I changed ",[61,365,366],{},"onLanguageSelect"," to carry the current page hash across, so ",[61,369,370],{},"#global-keywords"," on the English page lands you at ",[61,373,374],{},"\u002Fja-jp\u002Fci\u002Fyaml\u002F#global-keywords",". Translated pages keep English anchor IDs, so this works today; where a page has no matching anchor, it degrades to loading at the top instead of breaking. Shipped in September, three months ahead of launch, so it was never part of launch-day risk.",[16,377,378,389,390,393,394,396,397,399],{},[26,379,380,381,384,385,388],{},"Chasing the ",[61,382,383],{},"\u002Fhelp"," → ",[61,386,387],{},"docs.gitlab.com"," handoff."," The subtle risk wasn't the docs site — it stores no language preference — it was ",[61,391,392],{},"gitlab.com",", which does. A user with a Japanese preference on ",[61,395,392],{}," clicking through to the docs site crosses a domain boundary where browsers can do surprising things. Investigating this turned up a genuine constraint: the docs site has no staging environment beyond review apps, and ",[61,398,383],{}," is normally only exercised in GDK. So verification had to be assembled out of review apps and local GDK runs rather than a staging environment that did not exist.",[16,401,402,405,406,409],{},[26,403,404],{},"Launching on a clock, in the right timezone."," The deploy was scheduled for ",[26,407,408],{},"noon PST on Wednesday 10 December 2025 — 05:00 JST on the 11th",", so Japanese readers would wake up to it rather than watch it appear mid-afternoon. The launch was split into three tracked pieces with separate owners: site deployment, monitoring and QA during and after launch, and the announcement blog post. The GTM launch epic collected the QA rounds, bug fixes and internal and external communications in one place, so post-launch feedback had somewhere to land.",[11,411,216],{"id":215},[221,413,414,423,429,438],{},[224,415,416,422],{},[26,417,418,421],{},[61,419,420],{},"docs.gitlab.com\u002Fja-jp"," is live",", and Japanese is the first localized locale on GitLab's documentation site. The merge request merged on schedule on 10 December 2025.",[224,424,425,428],{},[26,426,427],{},"The language selector preserves your place in the page",", so switching language is a navigation action rather than a restart — a small fix with a disproportionate effect on whether the localized docs feel usable or merely present.",[224,430,431,434,435,437],{},[26,432,433],{},"A repeatable path for German and French."," The Hugo multilingual configuration, the selector behaviour, the ",[61,436,383],{}," handoff and the QA routine are all language-agnostic now. The next locale is a content problem, not an infrastructure problem — which was the actual point of doing the first one carefully.",[224,439,440,443],{},[26,441,442],{},"A documented launch pattern",": deployment, monitoring and announcement as three separately owned tracks with a fixed cutover time in the reader's timezone.",{"title":256,"searchDepth":257,"depth":257,"links":445},[446,447,448],{"id":13,"depth":257,"text":14},{"id":35,"depth":257,"text":36},{"id":215,"depth":257,"text":216},"\u002Fimages\u002Fprojects\u002Fplatform-japanese-documentation.svg","The GitLab documentation site with its language selector set to Japanese","Shipping docs.gitlab.com's first localized locale — the site plumbing, the language selector, and the launch itself.",[453,455,458],{"value":352,"label":454},"first localized locale live on docs.gitlab.com",{"value":456,"label":457},"10 Dec 2025","launch day — 05:00 JST on 11 December",{"value":459,"label":460},"3","tier-one languages on the programme roadmap: ja, de, fr",[462,464,467,470,473],{"label":420,"url":463},"https:\u002F\u002Fdocs.gitlab.com\u002Fja-jp\u002F",{"label":465,"url":466},"MR — Japanese Documentation","https:\u002F\u002Fgitlab.com\u002Fgitlab-org\u002Ftechnical-writing\u002Fdocs-gitlab-com\u002F-\u002Fmerge_requests\u002F1371",{"label":468,"url":469},"MR — Language selector anchor links","https:\u002F\u002Fgitlab.com\u002Fgitlab-org\u002Ftechnical-writing\u002Fdocs-gitlab-com\u002F-\u002Fmerge_requests\u002F1349",{"label":471,"url":472},"Programme epic","https:\u002F\u002Fgitlab.com\u002Fgroups\u002Fgitlab-com\u002Flocalization\u002F-\u002Fwork_items\u002F14",{"label":474,"url":475},"Handbook — tech docs localization","https:\u002F\u002Fhandbook.gitlab.com\u002Fhandbook\u002Fmarketing\u002Flocalization\u002Ftech_docs_localization\u002F",{},"\u002Fen\u002Fprojects\u002Fjapanese-documentation","2025 — 2026","Fullstack engineer, docs site localization",{"title":317,"description":451},[310,482,483,312,484,485],"Go templates","JavaScript","GitLab CI\u002FCD","GDK","en\u002Fprojects\u002Fjapanese-documentation","464x2KmVsZlcTFYHaTeS2Ni-kBOV338cqNWcR-8XUNA",{"id":489,"title":490,"body":491,"category":576,"cover":577,"coverAlt":578,"description":579,"draft":266,"extension":267,"externalUrl":268,"featured":266,"highlights":580,"links":593,"meta":597,"navigation":269,"order":598,"org":599,"path":600,"period":601,"role":602,"seo":603,"stack":604,"stem":610,"__hash__":611},"projects\u002Fen\u002Fprojects\u002Fxyz-textbooks.md","XYZ online textbook platform",{"type":8,"value":492,"toc":571},[493,495,501,508,515,517,523,529,539,541],[11,494,14],{"id":13},[16,496,497,498,500],{},"Anyone who has been stuck on a math problem at midnight knows the answer key doesn't help. Seeing the answer isn't the same as seeing ",[56,499,35],{},". What you actually want is someone sitting next to you, working the problem through, at your pace, as many times as it takes.",[16,502,503,504,507],{},"That is what XYZ Textbooks built. It's an interactive textbook where ",[26,505,506],{},"every math problem comes with three or four video tutorials of a real instructor teaching you how to solve it",". You pick the instructor whose explanation clicks for you, and you watch as much as you need: one step, the whole solution, or the same problem again from a different teacher.",[16,509,510,511,514],{},"It sounds simple for a student. It isn't simple to run. Hundreds of problems per book, times three or four videos each, across a catalog of more than 25 titles, adds up to ",[26,512,513],{},"terabytes of video"," that have to be stored, maintained and streamed on demand, the moment a student taps a problem.",[11,516,36],{"id":35},[16,518,519,522],{},[26,520,521],{},"Architecting for the video, together."," I worked alongside our lead engineer to architect the system that makes this possible: how tens of thousands of videos are organized, tied to the exact problem they teach, kept current as titles are revised, and delivered fast enough that a student never waits to get unstuck.",[16,524,525,528],{},[26,526,527],{},"Leading the UI."," When it came to building the experience students actually touch, that was mine to lead. The reader had to feel like a book, not a video portal: real chapters and page navigation, math that renders cleanly and stays legible at any zoom, and video sitting right at the problem where it helps rather than hidden in a separate \"resources\" tab. The hard part was choice without clutter. Three or four instructors per problem, across hundreds of problems, is a lot of video to put one tap away without burying the math itself.",[16,530,531,534,535,538],{},[26,532,533],{},"Designing the payment portal."," On top of the reader, I designed the entire payment portal: the purchase flow, the cart, and the account that connects a purchase to a student's bookshelf. Owning checkout meant XYZ set its own prices instead of accepting a courseware vendor's terms, and that still shows today: the Applied Calculus eBook sells for ",[26,536,537],{},"$45",", in a market where a textbook plus a mandatory access code routinely clears $200.",[11,540,216],{"id":215},[221,542,543,553,559,565],{},[224,544,545,548,549,552],{},[26,546,547],{},"13 years later, it's the same."," The UI I led is still the one students use on ",[61,550,551],{},"xyztextbooks.com"," today. For a web application built in 2013, that's the outcome that matters: it outlasted every framework trend that came after it.",[224,554,555,558],{},[26,556,557],{},"A teacher for every problem."," Students get a choice of real instructors on every problem in the book, not a single answer key, and they can watch as much as they need.",[224,560,561,564],{},[26,562,563],{},"Built to carry the weight."," A catalog of 25+ titles, with terabytes of video behind it, still served on demand from the architecture we designed.",[224,566,567,570],{},[26,568,569],{},"One product, end to end."," The reader, the video tutorials and the payment portal ship as one experience, with one account and one login.",{"title":256,"searchDepth":257,"depth":257,"links":572},[573,574,575],{"id":13,"depth":257,"text":14},{"id":35,"depth":257,"text":36},{"id":215,"depth":257,"text":216},"application","\u002Fimages\u002Fprojects\u002Fapplication-xyztextbooks.png","The XYZ Textbooks online reader showing a calculus chapter","An interactive math textbook where every problem comes with a real instructor teaching you how to solve it — terabytes of video, served on demand, through a UI that has lasted 13 years.",[581,584,587,590],{"value":582,"label":583},"3–4","video tutorials for every problem in the book",{"value":585,"label":586},"25+","titles in the catalog",{"value":588,"label":589},"Terabytes","of instructional video, served on demand",{"value":591,"label":592},"13 years","and the UI is still the one we shipped",[594],{"label":595,"url":596},"Applied Calculus on the platform","https:\u002F\u002Fwww.xyztextbooks.com\u002Febook\u002Ftitle\u002Fapplied_calculus",{},3,"XYZ Textbooks","\u002Fen\u002Fprojects\u002Fxyz-textbooks","2013 — 2015","Frontend engineering lead",{"title":490,"description":579},[483,605,606,607,608,609],"PHP","MySQL","Sass","MathJax","Payment gateway","en\u002Fprojects\u002Fxyz-textbooks","RaDTR47M7qhzjdk8uoS2vMZLjQ3mB57gSK9HjObXpvM",1791503629602]