mirror of
https://github.com/odoo/owl.git
synced 2025-10-06 19:59:41 +07:00
[ADD] Translation feature
This commit brings back the possibility to translate text nodes and the attributes "label", "title", "placeholder", and "alt" in an app configured with a suitable translation function. It is also possible to deactivate translations under a node via the directive t-translation="off". For flexibility it is possible to define the list of translatable attributes in the app.
This commit is contained in:
committed by
Géry Debongnie
parent
e2d98e4c26
commit
b8649f1add
+22
-14
@@ -1,23 +1,22 @@
|
||||
import {
|
||||
App,
|
||||
Component,
|
||||
onDestroyed,
|
||||
onWillPatch,
|
||||
onWillUnmount,
|
||||
onMounted,
|
||||
onPatched,
|
||||
onWillStart,
|
||||
onWillUpdateProps,
|
||||
useComponent,
|
||||
status,
|
||||
onRender,
|
||||
Component,
|
||||
onWillPatch,
|
||||
onWillStart,
|
||||
onWillUnmount,
|
||||
onWillUpdateProps,
|
||||
status,
|
||||
useComponent,
|
||||
} from "../src";
|
||||
import { TemplateSet, globalTemplates, UTILS } from "../src/qweb/template_helpers";
|
||||
import { blockDom } from "../src";
|
||||
import { BDom } from "../src/blockdom";
|
||||
// import { mountBlock } from "../src/blockdom/block";
|
||||
import { compileTemplate, Template } from "../src/qweb/compiler";
|
||||
import { blockDom } from "../src";
|
||||
import { CompileOptions, compileTemplate, Template } from "../src/qweb/compiler";
|
||||
import { globalTemplates, TemplateSet, UTILS } from "../src/qweb/template_helpers";
|
||||
import { xml } from "../src/tags";
|
||||
// import { UTILS } from "../src/template_utils";
|
||||
|
||||
const mount = blockDom.mount;
|
||||
|
||||
@@ -37,8 +36,17 @@ export function makeTestFixture() {
|
||||
return fixture;
|
||||
}
|
||||
|
||||
export function snapshotTemplateCode(template: string) {
|
||||
expect(compileTemplate(template).toString()).toMatchSnapshot();
|
||||
export function snapshotTemplateCode(template: string, options?: CompileOptions) {
|
||||
expect(compileTemplate(template, options).toString()).toMatchSnapshot();
|
||||
}
|
||||
|
||||
export function snapshotApp(app: App) {
|
||||
const Root = app.Root;
|
||||
const template = app.rawTemplates[Root.template];
|
||||
snapshotTemplateCode(template, {
|
||||
translateFn: app.translateFn,
|
||||
translatableAttributes: app.translatableAttributes,
|
||||
});
|
||||
}
|
||||
|
||||
export async function nextTick(): Promise<void> {
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`translation support can translate node content 1`] = `
|
||||
"function anonymous(bdom, helpers
|
||||
) {
|
||||
let { text, createBlock, list, multi, html, toggler, component } = bdom;
|
||||
let { withDefault, getTemplate, prepareList, withKey, zero, call, callSlot, capture, shallowEqual } = helpers;
|
||||
|
||||
let block1 = createBlock(\`<div>mot</div>\`);
|
||||
|
||||
return function template(ctx, node, key = \\"\\") {
|
||||
return block1();
|
||||
}
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`translation support does not translate node content if disabled 1`] = `
|
||||
"function anonymous(bdom, helpers
|
||||
) {
|
||||
let { text, createBlock, list, multi, html, toggler, component } = bdom;
|
||||
let { withDefault, getTemplate, prepareList, withKey, zero, call, callSlot, capture, shallowEqual } = helpers;
|
||||
|
||||
let block1 = createBlock(\`<div><span>mot</span><span>word</span></div>\`);
|
||||
|
||||
return function template(ctx, node, key = \\"\\") {
|
||||
return block1();
|
||||
}
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`translation support some attributes are translated 1`] = `
|
||||
"function anonymous(bdom, helpers
|
||||
) {
|
||||
let { text, createBlock, list, multi, html, toggler, component } = bdom;
|
||||
let { withDefault, getTemplate, prepareList, withKey, zero, call, callSlot, capture, shallowEqual } = helpers;
|
||||
|
||||
let block1 = createBlock(\`<div><p label=\\"mot\\">mot</p><p title=\\"mot\\">mot</p><p placeholder=\\"mot\\">mot</p><p alt=\\"mot\\">mot</p><p something=\\"word\\">mot</p></div>\`);
|
||||
|
||||
return function template(ctx, node, key = \\"\\") {
|
||||
return block1();
|
||||
}
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`translation support translation is done on the trimmed text, with extra spaces readded after 1`] = `
|
||||
"function anonymous(bdom, helpers
|
||||
) {
|
||||
let { text, createBlock, list, multi, html, toggler, component } = bdom;
|
||||
let { withDefault, getTemplate, prepareList, withKey, zero, call, callSlot, capture, shallowEqual } = helpers;
|
||||
|
||||
let block1 = createBlock(\`<div> mot </div>\`);
|
||||
|
||||
return function template(ctx, node, key = \\"\\") {
|
||||
return block1();
|
||||
}
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`translation support can set translatable attributes 1`] = `
|
||||
"function anonymous(bdom, helpers
|
||||
) {
|
||||
let { text, createBlock, list, multi, html, toggler, component } = bdom;
|
||||
let { withDefault, getTemplate, prepareList, withKey, zero, call, callSlot, capture, shallowEqual } = helpers;
|
||||
|
||||
let block1 = createBlock(\`<div tomato=\\"word\\" potato=\\"mot\\" title=\\"word\\">text</div>\`);
|
||||
|
||||
return function template(ctx, node, key = \\"\\") {
|
||||
return block1();
|
||||
}
|
||||
}"
|
||||
`;
|
||||
@@ -1126,4 +1126,47 @@ describe("qweb parser", () => {
|
||||
name: "myBlock",
|
||||
});
|
||||
});
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// t-translation
|
||||
// ---------------------------------------------------------------------------
|
||||
test('t-translation="off"', async () => {
|
||||
expect(parse(`<t t-translation="off">word</t>`)).toEqual({
|
||||
type: ASTType.TTranslation,
|
||||
content: {
|
||||
type: ASTType.Text,
|
||||
value: "word",
|
||||
},
|
||||
});
|
||||
|
||||
expect(parse(`<div t-foreach="list" t-translation="off" t-as="item">word</div>`)).toEqual({
|
||||
body: {
|
||||
content: {
|
||||
attrs: {},
|
||||
content: [
|
||||
{
|
||||
type: 0,
|
||||
value: "word",
|
||||
},
|
||||
],
|
||||
on: {},
|
||||
ref: null,
|
||||
tag: "div",
|
||||
type: 2,
|
||||
},
|
||||
type: 16,
|
||||
},
|
||||
collection: "list",
|
||||
elem: "item",
|
||||
hasNoComponent: true,
|
||||
hasNoFirst: true,
|
||||
hasNoIndex: true,
|
||||
hasNoLast: true,
|
||||
hasNoValue: true,
|
||||
isOnlyChild: false,
|
||||
key: null,
|
||||
memo: "",
|
||||
type: 9,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -0,0 +1,116 @@
|
||||
import { App, Component } from "../../src";
|
||||
import { makeTestFixture, snapshotApp } from "../helpers";
|
||||
import { xml } from "../../src/tags";
|
||||
|
||||
let fixture: HTMLElement;
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = makeTestFixture();
|
||||
});
|
||||
|
||||
describe("translation support", () => {
|
||||
test("can translate node content", async () => {
|
||||
class SomeComponent extends Component {
|
||||
static template = xml`<div>word</div>`;
|
||||
}
|
||||
|
||||
const app = new App(SomeComponent);
|
||||
app.configure({
|
||||
translateFn: (expr: string) => (expr === "word" ? "mot" : expr),
|
||||
});
|
||||
const comp = await app.mount(fixture);
|
||||
|
||||
const el = comp.el as HTMLElement;
|
||||
|
||||
expect(el.outerHTML).toBe("<div>mot</div>");
|
||||
snapshotApp(app);
|
||||
});
|
||||
|
||||
test("does not translate node content if disabled", async () => {
|
||||
class SomeComponent extends Component {
|
||||
static template = xml`
|
||||
<div>
|
||||
<span>word</span>
|
||||
<span t-translation="off">word</span>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
const app = new App(SomeComponent);
|
||||
app.configure({
|
||||
translateFn: (expr: string) => (expr === "word" ? "mot" : expr),
|
||||
});
|
||||
const comp = await app.mount(fixture);
|
||||
|
||||
const el = comp.el as HTMLElement;
|
||||
|
||||
expect(el.outerHTML).toBe("<div><span>mot</span><span>word</span></div>");
|
||||
snapshotApp(app);
|
||||
});
|
||||
|
||||
test("some attributes are translated", async () => {
|
||||
class SomeComponent extends Component {
|
||||
static template = xml`
|
||||
<div>
|
||||
<p label="word">word</p>
|
||||
<p title="word">word</p>
|
||||
<p placeholder="word">word</p>
|
||||
<p alt="word">word</p>
|
||||
<p something="word">word</p>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
const app = new App(SomeComponent);
|
||||
app.configure({
|
||||
translateFn: (expr: string) => (expr === "word" ? "mot" : expr),
|
||||
});
|
||||
const comp = await app.mount(fixture);
|
||||
|
||||
const el = comp.el as HTMLElement;
|
||||
|
||||
expect(el.outerHTML).toBe(
|
||||
'<div><p label="mot">mot</p><p title="mot">mot</p><p placeholder="mot">mot</p><p alt="mot">mot</p><p something="word">mot</p></div>'
|
||||
);
|
||||
snapshotApp(app);
|
||||
});
|
||||
|
||||
test("can set translatable attributes", async () => {
|
||||
class SomeComponent extends Component {
|
||||
static template = xml`
|
||||
<div tomato="word" potato="word" title="word">text</div>
|
||||
`;
|
||||
}
|
||||
|
||||
const app = new App(SomeComponent);
|
||||
app.configure({
|
||||
translateFn: (expr: string) => (expr === "word" ? "mot" : expr),
|
||||
translatableAttributes: ["potato"],
|
||||
});
|
||||
const comp = await app.mount(fixture);
|
||||
|
||||
const el = comp.el as HTMLElement;
|
||||
expect(el.outerHTML).toBe('<div tomato="word" potato="mot" title="word">text</div>');
|
||||
snapshotApp(app);
|
||||
});
|
||||
|
||||
test("translation is done on the trimmed text, with extra spaces readded after", async () => {
|
||||
class SomeComponent extends Component {
|
||||
static template = xml`
|
||||
<div> word </div>
|
||||
`;
|
||||
}
|
||||
|
||||
const translateFn = jest.fn((expr: string) => (expr === "word" ? "mot" : expr));
|
||||
|
||||
const app = new App(SomeComponent);
|
||||
app.configure({ translateFn });
|
||||
const comp = await app.mount(fixture);
|
||||
|
||||
const el = comp.el as HTMLElement;
|
||||
|
||||
expect(el.outerHTML).toBe("<div> mot </div>");
|
||||
expect(translateFn).toHaveBeenCalledWith("word");
|
||||
snapshotApp(app);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user