mirror of
https://github.com/odoo/owl.git
synced 2025-10-06 19:59:41 +07:00
a9be149e1e
A new directive t-tanslation-context and a new family of directives (of the form t-translation-context-...) are introduced to allow to translate terms in contexts. Set t-translation-context="fr" on a node makes every call of the translation function to be done with "fr" as seconde parameter when translating an attribute/content within that node or its children (if no closer t-translation-context directive is found). A directive t-translation-context-attr="fr" can be used on a node to target its attribute "attr". For example, if a div has an attribute title="a title", use t-translation-context-title="pt" will make "a title" to be translated in the context "pt". Note that this takes precedence over any other directive t-translation-context found on a parent (or the div itself). The translation function is in charge of the interpretation of the context: OWL does not associate any meaning with a translation context.
140 lines
5.2 KiB
TypeScript
140 lines
5.2 KiB
TypeScript
import { compile, Template, TemplateFunction } from "../compiler";
|
|
import { comment, createBlock, html, list, multi, text, toggler } from "./blockdom";
|
|
import { getCurrent } from "./component_node";
|
|
import { Portal, portalTemplate } from "./portal";
|
|
import { helpers } from "./template_helpers";
|
|
import { OwlError } from "../common/owl_error";
|
|
import { parseXML } from "../common/utils";
|
|
import type { customDirectives } from "../common/types";
|
|
|
|
const bdom = { text, createBlock, list, multi, html, toggler, comment };
|
|
|
|
export interface TemplateSetConfig {
|
|
dev?: boolean;
|
|
translatableAttributes?: string[];
|
|
translateFn?: (s: string, translationCtx: string) => string;
|
|
templates?: string | Document | Record<string, string>;
|
|
getTemplate?: (s: string) => Element | Function | string | void;
|
|
customDirectives?: customDirectives;
|
|
globalValues?: object;
|
|
}
|
|
|
|
export class TemplateSet {
|
|
static registerTemplate(name: string, fn: TemplateFunction) {
|
|
globalTemplates[name] = fn;
|
|
}
|
|
dev: boolean;
|
|
rawTemplates: typeof globalTemplates = Object.create(globalTemplates);
|
|
templates: { [name: string]: Template } = {};
|
|
getRawTemplate?: (s: string) => Element | Function | string | void;
|
|
translateFn?: (s: string, translationCtx: string) => string;
|
|
translatableAttributes?: string[];
|
|
Portal = Portal;
|
|
customDirectives: customDirectives;
|
|
runtimeUtils: object;
|
|
hasGlobalValues: boolean;
|
|
|
|
constructor(config: TemplateSetConfig = {}) {
|
|
this.dev = config.dev || false;
|
|
this.translateFn = config.translateFn;
|
|
this.translatableAttributes = config.translatableAttributes;
|
|
if (config.templates) {
|
|
if (config.templates instanceof Document || typeof config.templates === "string") {
|
|
this.addTemplates(config.templates);
|
|
} else {
|
|
for (const name in config.templates) {
|
|
this.addTemplate(name, config.templates[name]);
|
|
}
|
|
}
|
|
}
|
|
this.getRawTemplate = config.getTemplate;
|
|
this.customDirectives = config.customDirectives || {};
|
|
this.runtimeUtils = { ...helpers, __globals__: config.globalValues || {} };
|
|
this.hasGlobalValues = Boolean(config.globalValues && Object.keys(config.globalValues).length);
|
|
}
|
|
|
|
addTemplate(name: string, template: string | Element) {
|
|
if (name in this.rawTemplates) {
|
|
// this check can be expensive, just silently ignore double definitions outside dev mode
|
|
if (!this.dev) {
|
|
return;
|
|
}
|
|
const rawTemplate = this.rawTemplates[name];
|
|
const currentAsString =
|
|
typeof rawTemplate === "string"
|
|
? rawTemplate
|
|
: rawTemplate instanceof Element
|
|
? rawTemplate.outerHTML
|
|
: rawTemplate.toString();
|
|
const newAsString = typeof template === "string" ? template : template.outerHTML;
|
|
if (currentAsString === newAsString) {
|
|
return;
|
|
}
|
|
throw new OwlError(`Template ${name} already defined with different content`);
|
|
}
|
|
this.rawTemplates[name] = template;
|
|
}
|
|
|
|
addTemplates(xml: string | Document) {
|
|
if (!xml) {
|
|
// empty string
|
|
return;
|
|
}
|
|
xml = xml instanceof Document ? xml : parseXML(xml);
|
|
for (const template of xml.querySelectorAll("[t-name]")) {
|
|
const name = template.getAttribute("t-name")!;
|
|
this.addTemplate(name, template);
|
|
}
|
|
}
|
|
|
|
getTemplate(name: string): Template {
|
|
if (!(name in this.templates)) {
|
|
const rawTemplate = this.getRawTemplate?.(name) || this.rawTemplates[name];
|
|
if (rawTemplate === undefined) {
|
|
let extraInfo = "";
|
|
try {
|
|
const componentName = getCurrent().component.constructor.name;
|
|
extraInfo = ` (for component "${componentName}")`;
|
|
} catch {}
|
|
throw new OwlError(`Missing template: "${name}"${extraInfo}`);
|
|
}
|
|
const isFn = typeof rawTemplate === "function" && !(rawTemplate instanceof Element);
|
|
const templateFn = isFn ? rawTemplate : this._compileTemplate(name, rawTemplate);
|
|
// first add a function to lazily get the template, in case there is a
|
|
// recursive call to the template name
|
|
const templates = this.templates;
|
|
this.templates[name] = function (context, parent) {
|
|
return templates[name].call(this, context, parent);
|
|
};
|
|
const template = templateFn(this, bdom, this.runtimeUtils);
|
|
this.templates[name] = template;
|
|
}
|
|
return this.templates[name];
|
|
}
|
|
|
|
_compileTemplate(name: string, template: string | Element): ReturnType<typeof compile> {
|
|
throw new OwlError(`Unable to compile a template. Please use owl full build instead`);
|
|
}
|
|
|
|
callTemplate(owner: any, subTemplate: string, ctx: any, parent: any, key: any): any {
|
|
const template = this.getTemplate(subTemplate);
|
|
return toggler(subTemplate, template.call(owner, ctx, parent, key + subTemplate));
|
|
}
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// xml tag helper
|
|
// -----------------------------------------------------------------------------
|
|
export const globalTemplates: { [key: string]: string | Element | TemplateFunction } = {};
|
|
|
|
export function xml(...args: Parameters<typeof String.raw>) {
|
|
const name = `__template__${xml.nextId++}`;
|
|
const value = String.raw(...args);
|
|
globalTemplates[name] = value;
|
|
return name;
|
|
}
|
|
|
|
xml.nextId = 1;
|
|
|
|
TemplateSet.registerTemplate("__portal__", portalTemplate);
|